package br.com.srv.componentes.relatorio.controleitinerario.service;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import br.com.srv.commons.service.impl.BasicServiceImpl;
import br.com.srv.componentes.cerca.util.PontosTOComparator;
import br.com.srv.componentes.cerca.util.PontosTODataComparator;
import br.com.srv.componentes.cerca.util.VeiculoTOComparator;
import br.com.srv.componentes.jdbcdao.DadosLidoJdbcDAO;
import br.com.srv.componentes.relatorio.velocidademedia.util.GeometriaAnalitica;
import br.com.srv.componentes.rota.service.RotaService;
import br.com.srv.model.ControleItinerarioTO;
import br.com.srv.model.PeriodoTO;
import br.com.srv.model.PontoTO;
import br.com.srv.model.RotaTO;
import br.com.srv.model.VeiculoTO;
import br.com.srv.util.DateHelper;

public class ControleItinerarioServiceImpl
	extends BasicServiceImpl implements ControleItinerarioService {

	private DadosLidoJdbcDAO dadosLidoJdbcDAO;
	private RotaService rotaService;
	
	GeometriaAnalitica geometriaAnalitica = new GeometriaAnalitica();

	public Map<String, Object> consultaControleItinerario(
			ControleItinerarioTO controleItinerarioTO) throws Exception {

		List<ControleItinerarioTO> lista = new ArrayList<ControleItinerarioTO>(); 
		
		//contadores
		int quantidadeVeiculosTotal = 0;
		int quantidadeVeiculosComDados = 0;
		
		//Pegar a rota
		List<RotaTO> rotas = new ArrayList<RotaTO>();
		if (controleItinerarioTO.getRotaId() == null
				|| controleItinerarioTO.getRotaId().equals("")) {	
			RotaTO rotaTO = new RotaTO();
			rotaTO.setClienteId(controleItinerarioTO.getClienteId());
			rotas.addAll(getRotaService().buscarRotas(rotaTO));
		} else {
			rotas.add((RotaTO)getBasicDAO().findById(
					RotaTO.class, new Integer(controleItinerarioTO.getRotaId())));
		}
		
		for (RotaTO rotaTO : rotas) {			
		
			List<PontoTO> pontosRota = new ArrayList<PontoTO>();
			
			//ordena pontos da rota por data
			pontosRota.addAll(rotaTO.getPontosFiscalizacao());
			Collections.sort(pontosRota, new PontosTOComparator());				
			
			//pegar ponto inicial da rota
			PontoTO pontoInicio = pontosRota.get(0);
			
			//pegar ponto final da rota
			PontoTO pontoFim = pontosRota.get(pontosRota.size() - 1);
			
			//ordena veiculos por descricao
			List<VeiculoTO> veiculos = new ArrayList<VeiculoTO>();
			
			if (controleItinerarioTO.getVeiculoId() == null
					|| controleItinerarioTO.getVeiculoId().equals("")) {
				veiculos.addAll(rotaTO.getVeiculos());
				Collections.sort(veiculos, new VeiculoTOComparator());
			} else {
				veiculos.add((VeiculoTO)getBasicDAO().findById(
						VeiculoTO.class, new Integer(controleItinerarioTO.getVeiculoId())));
			}
			
			//somando total de ve�culos
			quantidadeVeiculosTotal += veiculos.size();
			
			for (VeiculoTO veiculoTO : veiculos) {
				
				boolean flagTransmitiu = false;				
				
				//passar data para formato aceito no DB.
				String dataInicial = DateHelper.parseStringDateToStringDateJdbc(
						controleItinerarioTO.getPeriodoTO().getDataInicial());
				String dataFinal = DateHelper.parseStringDateToStringDateJdbc(
						controleItinerarioTO.getPeriodoTO().getDataFinal());
				
				List<Map<String, String>> dados = getDadosLidoJdbcDAO().consultaPontosByVeiculo(veiculoTO.getRealId().toString(),
						dataInicial, dataFinal, controleItinerarioTO.getPeriodoTO().getHoraInicial(), controleItinerarioTO.getPeriodoTO().getHoraFinal());
				
				List<PontoTO> pontosVeiculo = gerarListaPontoTOs(dados);				
				
				//ordena pontos do veiculo por data
				Collections.sort(pontosVeiculo, new PontosTODataComparator());
				
				boolean flagInicio = false;
				long dintanciaPercorrida = 0;

				//guardar os pontos de final da rota.
				List<PontoTO> pontosFinais = new ArrayList<PontoTO>();
				
				ControleItinerarioTO controleItinerarioTO2 = new ControleItinerarioTO();
				
				boolean marcaVolta = false;
				
				for (PontoTO pontoVeiculo : pontosVeiculo) {
					
					if (geometriaAnalitica.distanciaEntrePontos(pontoVeiculo, pontoInicio)
							<= GeometriaAnalitica.PERTO) {
						
						marcaVolta = true;
						if (!flagTransmitiu) {
							flagTransmitiu = true;
							quantidadeVeiculosComDados++;
						}
						
						if (!flagInicio) {
							flagInicio = true;	
							controleItinerarioTO2 = new ControleItinerarioTO();
							controleItinerarioTO2.getPeriodoTO().setDataInicial(pontoVeiculo.getDataHora());
							controleItinerarioTO2.setDescricaoVeiculo(veiculoTO.getDescricao());
							controleItinerarioTO2.setVeiculoId(veiculoTO.getId().toString());
							controleItinerarioTO2.setDescricaoRota(rotaTO.getCodigo() + " - " + rotaTO.getNome());
							controleItinerarioTO2.setRotaId(rotaTO.getId().toString());
						}
						
					} else if (flagInicio && geometriaAnalitica.distanciaEntrePontos(pontoVeiculo, pontoFim)
							<= GeometriaAnalitica.PERTO) {					
						dintanciaPercorrida += pontoVeiculo.getDistanciaUltimoPonto();				
						pontosFinais.add(pontoVeiculo);
						
						if (marcaVolta) {
							controleItinerarioTO2.addVoltaCompleta();
							marcaVolta = false;
						}
						
						
					} else if (flagInicio){
						dintanciaPercorrida += pontoVeiculo.getDistanciaUltimoPonto();
					}				
				}
				
				
				//finalizar 
				if (!pontosFinais.isEmpty()) {
					PontoTO oPontoFinal = pontosFinais.get(pontosFinais.size()-1);
					dintanciaPercorrida += oPontoFinal.getDistanciaUltimoPonto();
					controleItinerarioTO2.getPeriodoTO().setDataFinal(oPontoFinal.getDataHora());
					controleItinerarioTO2.setDistanciaPercorrida(""+dintanciaPercorrida);
					controleItinerarioTO2.setTempoRealizado(calcularIntervalo(
							controleItinerarioTO2.getPeriodoTO().getDataInicial(), controleItinerarioTO2.getPeriodoTO().getDataFinal()));		
					lista.add(controleItinerarioTO2);
				} else if (flagInicio){
					controleItinerarioTO2.getPeriodoTO().setDataFinal("?");
					controleItinerarioTO2.setDistanciaPercorrida("?");		
					controleItinerarioTO2.setTempoRealizado("?");	
					lista.add(controleItinerarioTO2);
				} else {
					controleItinerarioTO2.getPeriodoTO().setDataInicial("?");
					controleItinerarioTO2.setDescricaoVeiculo(veiculoTO.getDescricao());
					controleItinerarioTO2.setDescricaoRota(rotaTO.getCodigo() + " - " + rotaTO.getNome());
					controleItinerarioTO2.getPeriodoTO().setDataFinal("?");
					controleItinerarioTO2.setDistanciaPercorrida("?");		
					controleItinerarioTO2.setTempoRealizado("?");
					controleItinerarioTO2.setNumeroVoltasCompletas(0);
					lista.add(controleItinerarioTO2);
				}
					
			}
		}
		
		Map<String, Object> mapa = new HashMap<String, Object>();
		mapa.put("lista", lista);
		mapa.put("quantidadeVeiculosTotal", quantidadeVeiculosTotal);
		mapa.put("quantidadeVeiculosComDados", quantidadeVeiculosComDados);
		mapa.put("quantidadeVeiculosSemDados", quantidadeVeiculosTotal-quantidadeVeiculosComDados);
		return mapa;
	}
	
	private String calcularIntervalo(String dataEntrada, String dataSaida) throws ParseException {		
		long entrada = DateHelper.parseDate(DateHelper.REPORT_DATE_DEFAULT, dataEntrada).getTime();
		long saida = DateHelper.parseDate(DateHelper.REPORT_DATE_DEFAULT, dataSaida).getTime();
		
		long emMinutos = ((saida - entrada)/1000)/60; 
		Long horas = emMinutos/60;
		long restoMinuto = emMinutos % 60;
		
		String result = "";
		
		if (horas < 10) {
			result += "0";
		}
		
		result +=  horas + ":";
		
		if (restoMinuto < 10){
			result += "0";
		}
		return  result + restoMinuto;
	}

	public List<ControleItinerarioTO> consultaControleItinerarioDetalhado(String rotaId,
			String veiculoId, String dataInicialStr, String dataFinalStr)
			throws ParseException, Exception {
		
		//lista para guardar o resultado
		List<ControleItinerarioTO> voltas = new ArrayList<ControleItinerarioTO>();
		
		//passar a data de string para java.util.Date
		Date dataInicial = new Date();
		try {
			dataInicial = DateHelper.parseDate(DateHelper.REPORT_DATE_DEFAULT, dataInicialStr);
		} catch (ParseException e) {
			//nada
		}
		
		//passar a data de string para java.util.Date
		Date dataFinal = new Date();
		try {
			dataFinal = DateHelper.parseDate(DateHelper.REPORT_DATE_DEFAULT, dataFinalStr);
		} catch (ParseException e) {
			//nada
		}
		
		//recuperar ve�culo
		VeiculoTO veiculoTO = (VeiculoTO)getBasicDAO().findById(
				VeiculoTO.class, new Integer(veiculoId));
		
		//recuperar rota
		RotaTO rotaTO = rotaService.buscarRotaById(rotaId);
		
		//ordena pontos da rota por sequencia
		List<PontoTO> pontosRota = new ArrayList<PontoTO>();
		pontosRota.addAll(rotaTO.getPontosFiscalizacao());
		Collections.sort(pontosRota, new PontosTOComparator());				
		
		//pegar ponto inicial da rota
		PontoTO pontoInicio = pontosRota.get(0);
		
		//pegar ponto final da rota
		PontoTO pontoFim = pontosRota.get(pontosRota.size() - 1);
		
		//******************************************************/		
		
		
		PeriodoTO periodoTO = new PeriodoTO();
		periodoTO.setDataInicial(DateHelper.parseString("yyyy-MM-dd", dataInicial));
		periodoTO.setHoraInicial(DateHelper.parseString("HH:mm:ss", dataInicial));
		periodoTO.setDataFinal(DateHelper.parseString("yyyy-MM-dd", dataFinal));
		periodoTO.setHoraFinal(DateHelper.parseString("HH:mm:ss", dataFinal));		
		
		List<Map<String, String>> dados = getDadosLidoJdbcDAO().consultaPontosByVeiculo(veiculoTO.getRealId().toString(),
				periodoTO.getDataInicial(), periodoTO.getDataFinal(), periodoTO.getHoraInicial(), periodoTO.getHoraFinal());		
		List<PontoTO> pontosVeiculo = gerarListaPontoTOs(dados);	
		
		//ordena pontos do veiculo por data
		Collections.sort(pontosVeiculo, new PontosTODataComparator());
				
		boolean flagInicio = false;

		//guardar os pontos de final da rota.
		List<PontoTO> pontosFinais = new ArrayList<PontoTO>();
		
		ControleItinerarioTO controleItinerarioTO = new ControleItinerarioTO();

		boolean marcaVolta = false;
		
		for (PontoTO pontoVeiculo : pontosVeiculo) {
			//inicio volta
			if (geometriaAnalitica.distanciaEntrePontos(pontoVeiculo, pontoInicio)
					<= GeometriaAnalitica.PERTO) {
				
				marcaVolta = true;
				
				if (!flagInicio) {
					flagInicio = true;	
					//instancia um novo TO
					controleItinerarioTO = new ControleItinerarioTO();
					controleItinerarioTO.getPeriodoTO().setDataInicial(pontoVeiculo.getDataHora());
				}
				
			//fim da volta
			} else if (flagInicio && geometriaAnalitica.distanciaEntrePontos(pontoVeiculo, pontoFim)
					<= GeometriaAnalitica.PERTO) {
				
				if (marcaVolta) {
										
					registraItinerario(controleItinerarioTO, pontoVeiculo);
					
					controleItinerarioTO.getPeriodoTO().setDataFinal(pontoVeiculo.getDataHora());
					
					//adiciona TO na laista de retorno
					voltas.add(controleItinerarioTO);
					
					flagInicio = false;
					marcaVolta = false;
				}
				
			//ponto na volta
			} else if (flagInicio){
				registraItinerario(controleItinerarioTO, pontoVeiculo);
			}
		}
		
		if (flagInicio){			
			controleItinerarioTO.getPeriodoTO().setDataFinal("?");	
			voltas.add(controleItinerarioTO);
			
			flagInicio = false;
		}
		
		return voltas;
	}

	private void registraItinerario(ControleItinerarioTO controleItinerarioTO, PontoTO pontoVeiculo) {
		boolean podeRegistar = true;
		List<Map> list = controleItinerarioTO.getItinerario();
		if (list != null && !list.isEmpty()) {
			podeRegistar = !list.get(list.size()-1).get("descricao").equals(pontoVeiculo.getDescricao());
		}
		
		if (podeRegistar) {
			Map<String, String> mapa = new HashMap<String, String>();
			mapa.put("descricao", pontoVeiculo.getDescricao());
			mapa.put("hora", pontoVeiculo.getDataHora());
			controleItinerarioTO.addItinerario(mapa);
		}
	}
	
	public RotaService getRotaService() {
		return rotaService;
	}

	public void setRotaService(RotaService rotaService) {
		this.rotaService = rotaService;
	}

	public DadosLidoJdbcDAO getDadosLidoJdbcDAO() {
		return dadosLidoJdbcDAO;
	}

	public void setDadosLidoJdbcDAO(DadosLidoJdbcDAO dadosLidoJdbcDAO) {
		this.dadosLidoJdbcDAO = dadosLidoJdbcDAO;
	}
	
	private List<PontoTO> gerarListaPontoTOs(List<Map<String, String>> pontos) {
		List<PontoTO> pontosVeiculo = new ArrayList<PontoTO>();
		for (Map mapa : pontos) {
			PontoTO pontoTO = new PontoTO();
			pontoTO.setDataHora((String)mapa.get("DATE_TIME"));
			pontoTO.setLongitude(new Double((String)mapa.get("X")));
			pontoTO.setLatitude(new Double((String)mapa.get("Y")));	
			pontoTO.setVelocidade(new Double((String)mapa.get("VEL")));	
			pontoTO.setDistanciaUltimoPonto(new Double((String)mapa.get("DIST_BETWEEN_POSITIONS")));
			pontoTO.setDescricao((String)mapa.get("NEAR_DESC"));
			pontosVeiculo.add(pontoTO);
		}
		return pontosVeiculo;
	}
	
}
